package com.jtcl.config;

import com.jtcl.enums.OrderEvent;
import com.jtcl.enums.OrderState;
import com.jtcl.module.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.squirrelframework.foundation.fsm.StateMachineStatus;
import org.squirrelframework.foundation.fsm.UntypedStateMachine;
import org.squirrelframework.foundation.fsm.annotation.*;
import org.squirrelframework.foundation.fsm.impl.AbstractStateMachine;

/**
 * @author sjt
 * @since 2021-02-26 11:58
 */

/**
 * 该类相当于监听状态变化，构造一些监听方法实现一些逻辑代码
 *
 * @States 定义状态列表，里面可以包含多个状态
 * @State定义每个状态，name状态名称，entryStateInit进入状态时调用的方法，exitCallMethod 离开状态是调用的方法，initialState 为true时，为默认状态。
 */
@States({
        @State(name = "UNFOUND", entryCallMethod = "entryStateInit", exitCallMethod = "exitStateInit", initialState = true),
        @State(name = "USING", entryCallMethod = "entryStateWaitPay", exitCallMethod = "exitStateWaitPay"),
        @State(name = "COMPLETE", entryCallMethod = "entryStateWaitSend", exitCallMethod = "exitStateWaitSend"),
        @State(name = "REFUND", entryCallMethod = "entryStatePartSend", exitCallMethod = "exitStatePartSend"),
        @State(name = "NOUSE", entryCallMethod = "entryStatePartSend", exitCallMethod = "exitStatePartSend")
})
@Transitions({
        @Transit(from = "UNFOUND", to = "UNFOUND", on = "FOUND", callMethod = "createOrder"),
        @Transit(from = "UNFOUND", to = "USING", on = "SAOMA", callMethod = "submitOrder"),
        @Transit(from = "USING", to = "COMPLETE", on = "PAY", callMethod = "pay"),
        @Transit(from = "USING", to = "REFUND", on = "USING_REFUNDING", callMethod = "usingRefund"),
        @Transit(from = "COMPLETE", to = "REFUND", on = "COM_REFUNDING", callMethod = "comRefund")
})
//该地方向AbstractStateMachine传递的参数
@StateMachineParameters(stateType = OrderState.class, eventType = OrderEvent.class, contextType = OrderContext.class)
@Slf4j
public class SubmitOrderStateMachine extends AbstractStateMachine<UntypedStateMachine, Object, Object, Object> implements UntypedStateMachine {

    private OrderService orderService;
    protected ApplicationContext applicationContext;

    //定义构造函数接受ApplicationContext注入([参看New State Machine Instance](http://hekailiang.github.io/squirrel/))
    public SubmitOrderStateMachine(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        // 通过applicationContext注入orderService，这样就可以通过service操作数据库
        orderService = (OrderService) this.applicationContext.getBean("orderService");
    }

    //创建订单，依旧处于待使用状态
    public void createOrder(OrderState fromState, OrderState toState, OrderEvent OrderEvent, OrderContext OrderContext) {
        //可以做一些创建订单等等操作
    }

    //提交订单
    public void submitOrder(OrderState fromState, OrderState toState, OrderEvent OrderEvent, OrderContext OrderContext) {
    }

    //支付订单
    public void pay(OrderState fromState, OrderState toState, OrderEvent OrderEvent, OrderContext OrderContext) {
    }

    public void usingRefund(OrderState fromState, OrderState toState, OrderEvent OrderEvent, OrderContext OrderContext) {
    }

    public void comRefund(OrderState fromState, OrderState toState, OrderEvent OrderEvent, OrderContext OrderContext) {
    }

    /**
     * 如果实现这个方法，当上面方法执行出现错误时就会转到这里来执行。
     * 这里并未实现，具体的实现方法请参考官网
     */
    @Override
    protected void afterTransitionCausedException(Object fromState, Object toState, Object event, Object context) {
        log.info("状态机运行出现异常");
        Throwable targeException = getLastException().getTargetException();
        // recover from IllegalArgumentException thrown out from state 'A' to 'B' caused by event 'ToB'
        if (targeException instanceof IllegalArgumentException &&
                fromState.equals("A") && toState.equals("B") && event.equals("ToB")) {
            // 在这里做一些错误处理工作
            // ...
            // 从此异常中恢复后，将状态机状态重置为正常
            setStatus(StateMachineStatus.IDLE);
            log.info("状态机异常已处理，状态已恢复");
        } else {
            super.afterTransitionCausedException(fromState, toState, event, context);
        }
    }
}
